/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */

using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Xsl;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
using System.Text;
using System.Security.Policy;

namespace ELE.Common.Util
{

  /// <summary> This is a utility for handling XML files that uses DOM.
  /// 
  /// </summary>
  /// <version>   1.0 30 Jun 2003
  /// </version>
  /// <author>    Miguel Curi
  /// 
  /// </author>
  public class XML
  {
    /// <summary>
    /// 
    /// </summary>
    public const int PARSE_SUCCESS = 0;
    /// <summary>
    /// 
    /// </summary>
    public const int PARSE_ERROR = 1;
    /// <summary>
    /// 
    /// </summary>
    public XML()
    {
    }
    // Display the validation error.
    static void ValidationCallback(object sender, ValidationEventArgs args)
    {
      Console.WriteLine("Validation error loading xml");
      Console.WriteLine(args.Message);
    }
    /// <summary>
    /// Parses an xml document from a string
    /// </summary>
    /// <param name="srcXml"></param>
    /// <returns>The document object</returns>
    public static XmlDocument ParseFromString(string srcXml)
    {
      StringReader tr = new StringReader(srcXml);
      XmlTextReader sr = new XmlTextReader(tr);
      try
      {
        return Parse(sr);
      }
      finally
      {
        tr.Close();
        sr.Close();
      }
    }
    /// <summary> Parsing to DOM
    /// 
    /// </summary>
    /// <param name="fileName">the name of the file to parse
    /// </param>
    /// <returns> The DOM document 
    /// </returns>
    public static XmlDocument Parse(String fileName)
    {
      return Parse(fileName, false);
    }
    /// <summary> 
    /// Parsing to DOM 
    /// </summary>
    /// <param name="fileName">the name of the file to parse
    /// </param>
    /// <param name="validate">whether to use auto validation
    /// </param>
    /// <returns> The DOM document
    /// </returns>
    public static XmlDocument Parse(String fileName, bool validate)
    {
      Stream fis = null;
      if (fileName.StartsWith(FileUtil.EMBEDDED_RESOURCE))
      {
        fis = FileUtil.ReadEmbeddedResource(fileName);
      }
      else
      {
        fis = new FileStream(fileName, FileMode.Open, FileAccess.Read);
      }

      return Parse(fis, validate);
    }

    /// <summary> Parsing to DOM
    /// 
    /// </summary>
    /// <param name="inputStream">The input stream
    /// </param>
    /// <returns> The DOM document
    /// 
    /// </returns>
    public static XmlDocument Parse(Stream inputStream)
    {
      return Parse(inputStream, false);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="txtReader"></param>
    /// <returns></returns>
    public static XmlDocument Parse(XmlTextReader txtReader)
    {
      return Parse(txtReader, false);
    }
    /// <summary>
    /// Parses an xml from an xml text reader
    /// </summary>
    /// <param name="txtReader"></param>
    /// <param name="validate"></param>
    /// <returns></returns>
    public static XmlDocument Parse(XmlTextReader txtReader, bool validate)
    {
      XmlDocument doc = new XmlDocument();
      if (validate)
      {
        XmlReader reader = null;
        try
        {
          XmlReaderSettings settings = new XmlReaderSettings();
          ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationCallback);
          settings.ValidationEventHandler += eventHandler;
          settings.ValidationType = ValidationType.Schema;
          reader = XmlReader.Create(txtReader, settings);
          doc.Load(reader);
        }
        finally
        {
          if (reader != null)
            reader.Close();
        }
      }
      else
      {
        doc.Load(txtReader);
      }

      //Console.WriteLine(doc.OuterXml);
      return doc;
    }
    /// <summary> Parsing to DOM
    /// </summary>
    /// <param name="inputStream">The input stream (will be closed after read)
    /// </param>
    /// <param name="validate">whether to use auto validation
    /// </param>
    /// <returns> The DOM document
    /// 
    /// </returns>
    public static XmlDocument Parse(Stream inputStream, bool validate)
    {
      if (inputStream == null)
        return null;
      XmlTextReader txtReader = null;
      try
      {

        txtReader = new XmlTextReader(inputStream);

        // Create the validating reader and specify Auto validation.
        return Parse(txtReader, validate);
      }
      catch (Exception e)
      {
        throw e;
      }
      finally
      {
        if (txtReader != null)
          txtReader.Close();
        inputStream.Close();
      }
    }

    /// <summary> Parsing to DOM
    /// 
    /// </summary>
    /// <param name="content">the content to parse as XML
    /// </param>
    /// <returns> The DOM document
    /// 
    /// </returns>
    public static XmlDocument Parse(byte[] content)
    {
      return Parse(content, false);
    }

    /// <summary> Parsing to DOM
    /// 
    /// </summary>
    /// <param name="content">the content to parse as XML
    /// </param>
    /// <param name="validate"></param>
    /// <returns> The DOM document
    /// 
    /// </returns>
    public static XmlDocument Parse(byte[] content, bool validate)
    {
      MemoryStream ms = new MemoryStream(content);
      return Parse(ms, validate);
    }
    /// <summary> Writing a DOM Tree to a file
    /// </summary>
    /// <param name="fileName">The fully qualified name of the xml file
    /// </param>
    /// <param name="doc">The document in DOM form
    /// 
    /// </param>
    public static void Write(XmlDocument doc, String fileName)
    {
      XmlTextWriter writer = null;
      try
      {
        writer = new XmlTextWriter(fileName, System.Text.Encoding.UTF8);
        Write(doc, writer);
      }
      catch (Exception e)
      {
        throw e;
      }
      finally
      {
        if (writer != null)
        {
          writer.Close();
        }
      }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="doc"></param>
    /// <param name="outWriter"></param>
    public static void Write(XmlDocument doc, TextWriter outWriter)
    {
      XmlTextWriter writer = null;
      try
      {
        writer = new XmlTextWriter(outWriter);
        Write(doc, writer);
      }
      catch (Exception e)
      {
        throw e;
      }
      finally
      {
        if (writer != null)
        {
          writer.Close();
        }
      }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="doc"></param>
    /// <param name="outWriter"></param>
    public static void Write(XmlDocument doc, XmlTextWriter outWriter)
    {
      try
      {
        outWriter.Formatting = Formatting.Indented;
        outWriter.Indentation = 2;
        outWriter.QuoteChar = '\u0027';
        doc.WriteTo(outWriter);
        outWriter.Flush();
      }
      catch (Exception e)
      {
        throw e;
      }
      finally
      {
        if (outWriter != null)
        {
          outWriter.Close();
        }
      }
    }

    /// <summary>
    /// Converts an object to another of specified through XML Serialization.  
    /// This is used primarily to convert web service proxy classes back to 
    /// their originial form.  In order for this to work, both types must belong
    /// to the same XML namespace.  This is done throug the use of the following
    /// attributes:
    /// 
    /// On the web service:
    /// [WebService(Namespace="http://Octl.ssofi/")]
    /// 
    /// On the bean class:
    /// [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://Octl.ssofi/")]
    /// </summary>
    /// <param name="oin">object to convert</param>
    /// <param name="t">type to convert object to</param>
    /// <returns>new object of supplied type</returns>
    public static Object ConvertObject(Object oin, System.Type t)
    {
      StringBuilder sb = new StringBuilder();
      StringWriter writer = new StringWriter(sb);
      XmlSerializer sin = new XmlSerializer(oin.GetType());
      sin.Serialize(writer, oin);
      writer.Close();

      XmlSerializer sout = new XmlSerializer(t);
      StringReader reader = new StringReader(sb.ToString());
      Object oout = sout.Deserialize(reader);
      reader.Close();
      return oout;
    }

    /// <summary>
    /// Serialize object to XML string
    /// </summary>
    /// <param name="pObject">object to serialize</param>
    /// <returns>returns XML string representation of object</returns>
    public static string SerializeObjectToXmlString(Object pObject)
    {
      if (pObject == null)
        return null;

      StringWriter stringWriter = null;
      XmlTextWriter xt = null;
      try
      {
        XmlSerializer serializer = new XmlSerializer(pObject.GetType());
        StringBuilder sb = new StringBuilder();
        stringWriter = new StringWriter(sb);
        xt = new XmlTextWriter(stringWriter);
        xt.Formatting = Formatting.None;
        serializer.Serialize(xt, pObject);
        return sb.ToString();
      }
      finally
      {
        if (stringWriter != null)
          stringWriter.Close();

        if (xt != null)
          xt.Close();
      }
    }

    /// <summary>
    /// Serializes an object into an xml document in memory
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <returns>The xml document from the object</returns>
    public static XmlDocument SerializeObject(Object pObject)
    {
      if (pObject != null)
      {
        StringBuilder sb = new StringBuilder();
        StringWriter writer = null;
        try
        {
          writer = new StringWriter(sb);
          XmlSerializer sin = new XmlSerializer(pObject.GetType());
          sin.Serialize(writer, pObject);
        }
        catch (Exception e)
        {
          throw e;
        }
        finally
        {
          if (writer != null)
          {
            writer.Close();
          }
        }
        StringReader tr = new StringReader(sb.ToString());
        XmlTextReader sr = new XmlTextReader(tr);
        return Parse(sr);
      }
      return null;
    }
    /// <summary>
    /// Serializes an object into an xml document at the given path or file name using UTF-8 Encoding
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <param name="pPath">The path to the file we want to write</param>
    public static XmlDocument SerializeObject(Object pObject, string pPath)
    {
      return SerializeObject(pObject, pPath, Encoding.UTF8);
    }
    /// <summary>
    /// Serializes an object into an xml document at the given path or file name using UTF-8 Encoding
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <param name="pPath">The path to the file we want to write</param>
    /// <param name="namespaces">The namespaces to use for serialization</param>
    /// <returns></returns>
    public static XmlDocument SerializeObject(Object pObject, string pPath, XmlSerializerNamespaces namespaces)
    {
      return SerializeObject(pObject, pPath, Encoding.UTF8, namespaces);
    }
    /// <summary>
    /// Serializes an object into an xml document at the given path or file name
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <param name="pPath">The path to the file we want to write</param>
    /// <param name="pEncoding">The encoding type to use i.e. utf-8</param>
    public static XmlDocument SerializeObject(Object pObject, string pPath, Encoding pEncoding)
    {
      return SerializeObject(pObject, pPath, pEncoding, null);
    }
    /// <summary>
    /// Serializes an object into an xml document at the given path or file name
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <param name="pPath">The path to the file we want to write</param>
    /// <param name="pSettings">The settings of the writer for formatting</param>
    public static XmlDocument SerializeObject(Object pObject, string pPath, XmlWriterSettings pSettings)
    {
      return SerializeObject(pObject, pPath, pSettings, null);
    }
    /// <summary>
    /// Serializes an object into an xml document at the given path or file name
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <param name="pPath">The path to the file we want to write</param>
    /// <param name="pSettings">The settings of the writer for formatting</param>
    /// <param name="pNamespaces">Namespaces to use when serializing</param>
    public static XmlDocument SerializeObject(Object pObject, string pPath, XmlWriterSettings pSettings, XmlSerializerNamespaces pNamespaces)
    {
      if (pObject != null)
      {
        XmlWriter writer = null;
        try
        {
          writer = XmlTextWriter.Create(pPath, pSettings);
          XmlSerializer sin = new XmlSerializer(pObject.GetType());
          if (pNamespaces == null)
          {
            sin.Serialize(writer, pObject);
          }
          else
          {
            sin.Serialize(writer, pObject, pNamespaces);
          }
        }
        catch (Exception e)
        {
          throw e;
        }
        finally
        {
          if (writer != null)
          {
            writer.Close();
          }
        }
        return Parse(pPath);
      }
      return null;
    }
    /// <summary>
    /// Serializes an object into an xml document at the given path or file name
    /// </summary>
    /// <param name="pObject">the object that we want to serialize</param>
    /// <param name="pPath">The path to the file we want to write</param>
    /// <param name="pEncoding">The encoding type to use i.e. utf-8</param>
    /// <param name="namespaces">Namesapces to use when serializing</param>
    public static XmlDocument SerializeObject(Object pObject, string pPath, Encoding pEncoding, XmlSerializerNamespaces namespaces)
    {

      XmlWriterSettings writerSettings = new XmlWriterSettings();
      writerSettings.Indent = true;
      writerSettings.IndentChars = "  ";
      writerSettings.Encoding = pEncoding;
      return SerializeObject(pObject, pPath, writerSettings, namespaces);
    }
    /// <summary>
    /// Deserializes an object from a file located at the given path
    /// </summary>
    /// <param name="pObjectType"></param>
    /// <param name="pPath"></param>
    /// <returns></returns>
    public static object DeSerializeObject(Type pObjectType, string pPath)
    {
      Stream s = null;
      try
      {
        XmlSerializer serializer = new XmlSerializer(pObjectType);
        s = new FileStream(pPath, FileMode.Open, FileAccess.Read);
        return serializer.Deserialize(s);
      }
      catch (Exception e)
      {
        throw e;
      }
      finally
      {
        if (s != null)
        {
          s.Close();
        }
      }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="oin"></param>
    /// <param name="t"></param>
    /// <returns></returns>
    public static Object[] ConvertObjects(Object[] oin, System.Type t)
    {
      if (oin == null)
        return null;

      ArrayList oout = new ArrayList();
      foreach (Object o in oin)
      {
        oout.Add(XML.ConvertObject(o, t));
      }

      return (Object[])oout.ToArray(t);
    }
    /// <summary>
    /// Transforms a document from a specified location using a stylesheet from a given 
    /// location and writing the results to the given target location
    /// </summary>
    /// <param name="srcDoc">location of the source document</param>
    /// <param name="stylesheet">location of the stylesheet</param>
    /// <param name="targetDoc">location of the target results</param>
    public static void Transform(string srcDoc, string stylesheet, string targetDoc)
    {
      XslCompiledTransform transform = new XslCompiledTransform();
      transform.Load(stylesheet);
      transform.Transform(srcDoc, targetDoc);
    }
    /// <summary>
    /// XSLT tranformation utility
    /// </summary>
    /// <param name="srcDoc"></param>
    /// <param name="styleSheet"></param>
    /// <returns></returns>
    public static XmlDocument Transform(XmlDocument srcDoc, XmlDocument styleSheet)
    {

      return Transform(srcDoc, styleSheet, null);
    }
    /// <summary>
    /// XSLT tranformation utility
    /// </summary>
    /// <param name="srcDoc"></param>
    /// <param name="styleSheet"></param>
    /// <param name="resolver"></param>
    /// <returns></returns>
    public static XmlDocument Transform(XmlDocument srcDoc, XmlDocument styleSheet, XmlResolver resolver)
    {

      return Transform(srcDoc, styleSheet, null, resolver);
    }
    /// <summary>
    /// XSLT tranformation utility
    /// </summary>
    /// <param name="srcDoc"></param>
    /// <param name="styleSheet"></param>
    /// <param name="arguments"></param>
    /// <param name="resolver"></param>
    /// <returns></returns>
    public static XmlDocument Transform(XmlDocument srcDoc,
                                          XmlDocument styleSheet,
                                          XsltArgumentList arguments,
                                          XmlResolver resolver)
    {
      StringWriter sw = null;
      XmlTextWriter writer = null;
      try
      {
        StringBuilder sb = new StringBuilder();
        sw = new StringWriter(sb);
        writer = new XmlTextWriter(sw);
        XslCompiledTransform xsltransform = new XslCompiledTransform();
        XsltSettings settings = new XsltSettings();
        xsltransform.Load(styleSheet, settings, resolver);
        xsltransform.Transform(srcDoc, arguments, writer);
        return ParseFromString(sb.ToString());
      }
      finally
      {
        if (sw != null)
        {
          sw.Close();
        }
        if (writer != null)
        {
          writer.Close();
        }
      }
    }

    /// <summary>
    /// Extract a node by name ignoring case. First try is the name as given, 
    /// then all lowercase, then all uppercase, then capitalized
    /// </summary>
    /// <param name="pParentNode">The parent node</param>
    /// <param name="pXPath">the xpath expression to find the node from the parent</param>
    /// <returns>The first node found in the parent with the given name ignoring case</returns>
    protected static XmlNode SelectSingleNodeIgnoreCase(XmlNode pParentNode, string pXPath)
    {
      XmlNode node = null;
      node = pParentNode.SelectSingleNode(pXPath);
      if (node == null)
      {
        node = pParentNode.SelectSingleNode(pXPath.ToLower());
      }
      if (node == null)
      {
        node = pParentNode.SelectSingleNode(pXPath.ToUpper());
      }
      if (node == null)
      {
        node = pParentNode.SelectSingleNode(StringUtils.Capitalize(pXPath));
      }
      return node;
    }
    /// <summary>
    /// Extract a list of nodes by name ignoring case.
    /// First try is the name as given, then all uppercase, then capitalized
    /// </summary>
    /// <param name="pParentNode">The parent node</param>
    /// <param name="pXPath">the name</param>
    /// <returns>The list of nodes found in the parent with the given name ignoring case</returns>
    public static IList SelectNodesIgnoreCase(XmlNode pParentNode, string pXPath)
    {
      IList nodes = new ArrayList();
      XmlNodeList nodeList = pParentNode.SelectNodes(pXPath);

      if (!pXPath.Equals(pXPath.ToLower()))
      {
        XmlNodeList nodeListLower = pParentNode.SelectNodes(pXPath.ToLower());
        if (nodeListLower != null)
        {
          foreach (XmlNode node in nodeListLower)
          {
            nodes.Add(node);
          }
        }
      }
      if (!pXPath.Equals(pXPath.ToUpper()))
      {
        XmlNodeList nodeListUpper = pParentNode.SelectNodes(pXPath.ToUpper());
        if (nodeListUpper != null)
        {
          foreach (XmlNode node in nodeListUpper)
          {
            nodes.Add(node);
          }
        }
      }
      if (!pXPath.Equals(StringUtils.Capitalize(pXPath)))
      {
        XmlNodeList nodeListCaps = pParentNode.SelectNodes(StringUtils.Capitalize(pXPath));
        if (nodeListCaps != null)
        {
          foreach (XmlNode node in nodeListCaps)
          {
            nodes.Add(node);
          }
        }
      }
      if (nodeList != null)
      {
        foreach (XmlNode node in nodeList)
        {
          nodes.Add(node);
        }
      }
      return nodes;
    }
    /// <summary> Main method for testing purposes
    /// </summary>
    [STAThread]
    public static void Main(String[] args)
    {

      if (args.Length <= 0)
      {
        Console.Out.WriteLine("Usage: .exe args");
        return;
      }

      String url = args[0];

      try
      {
        XmlDocument doc = XML.Parse(url);
        XML.Write(doc, Console.Out);
        // Process the root element
      }
      catch
      {
        Console.Out.WriteLine(url + " IO Exception.");
      }
    }
    // end main
  }
}
